<?php

declare(strict_types=1);

namespace App\Master\Framework\Library\Wechat;


use App\Master\Enum\RedisKeyEnum;
use App\Master\Framework\Extend\Module;
use App\Master\Framework\Library\Library;
use App\Utils\Common;
use App\Utils\Control\AuthSite;
use App\Utils\RedisUtil;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Facades\Cache;

/**
 * 扩展返回工具
 */
class OfficialLibrary extends Library
{
    protected string $base_uri = 'https://api.weixin.qq.com';
    protected array  $config   = [];

    /**
     * 实例化
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
     */
    public function __construct()
    {
        // 微信开发配置
        $_table = "WechatConfig";
        $config = Module::_SetupModule("WechatConfig")['official'] ?? [];
        $this->config = [
            'app_id'  => $config['app_id'] ?? '',
            'secret'  => $config['app_secret'] ?? '',
            'token'   => $config['token'] ?? '',
            'aes_key' => $config['aes_key'] ?? '',
        ];
    }

    /**
     * 获取code
     * 注意：此方法返回组合连接，并不会主动重定向，需要使用者自己执行
     * @param string $redirect_uri 重定向地址
     * @param array $data 传递参数
     * @param int $type 网页授权类型 1=静默授权，2=非静默授权
     * @return void
     */
    public function code(string $redirect_uri, mixed $data = [], int $type = 2)
    {
        $url = 'https://open.weixin.qq.com/connect/oauth2/authorize';
        //授权方式：snsapi_base：静默授权，snsapi_userinfo高级授权
        $scope = $type === 1 ? 'snsapi_base' : 'snsapi_userinfo';
        $params = [
            'appid'         => $this->config['app_id'],
            'redirect_uri'  => $redirect_uri,
            'response_type' => 'code',
            'scope'         => $scope,
            'state'         => is_array($data) ? json_encode($data) : $data,
            '#wechat_redirect'
        ];
        header("Location: " . $url . '?' . http_build_query($params));
    }

    /**
     * 授权
     * @param $code
     * @return bool
     */
    public function jscode2session($code)
    {
        $data = [
            'appid'      => $this->config['app_id'],
            'secret'     => $this->config['secret'],
            'code'       => $code,
            'grant_type' => 'authorization_code',
        ];
        $response = $this->getJson("/sns/oauth2/access_token", $data);
        if ($response->getStatusCode() != 200) {
            return $this->error($response->getReasonPhrase());
        }

        $json = $response->getBody()->getContents();
        $body = json_decode($json, true);
        if (!isset($body['openid'])) {
            return $this->error(!empty($body['errmsg']) ? $body['errmsg'] : 'Error_0', $body);
        }

        return $this->success('获取成功', $body);
    }

    /**
     * 拉取用户信息
     * @param string $openid
     * @param string $access_token
     * @return bool
     */
    public function userInfo(string $openid, string $access_token)
    {
        $data = [
            'access_token' => $access_token,
            'openid'       => $openid,
            'lang'         => 'zh_CN',
        ];
        $response = $this->getJson("/sns/userinfo", $data);
        if ($response->getStatusCode() != 200) {
            return $this->error($response->getReasonPhrase());
        }

        $json = $response->getBody()->getContents();
        $body = json_decode($json, true);
        if (!isset($body['openid'])) {
            return $this->error(!empty($body['errmsg']) ? $body['errmsg'] : 'Error_0', $body);
        }

        return $this->success('获取成功', $body);
    }

    /**
     * JS-SDK
     * @param string $url
     * @return bool
     */
    public function jssdkBuildConfig(string $url)
    {
        if (!$this->getTicket()) {
            return $this->error($this->getMessage(), $this->getData());
        }
        $jsapiTicket = $this->getData();
        $timestamp = time();
        $nonceStr = Common::createNonceStr();
        // 这里参数的顺序要按照 key 值 ASCII 码升序排序
        $string = "jsapi_ticket={$jsapiTicket}&noncestr={$nonceStr}&timestamp={$timestamp}&url={$url}";
        $signature = sha1($string);
        $signPackage = [
            "appId"     => $this->config['app_id'],
            "nonceStr"  => $nonceStr,
            "timestamp" => $timestamp,
            "url"       => $url,
            "signature" => $signature,
            "rawString" => $string
        ];
        return $this->success('success', $signPackage);
    }

    /**
     * 获取微信jsapi_ticket
     * @param bool $refresh 是否更新
     * @return bool
     */
    private function getTicket(bool $refresh = false)
    {
        //有效期7200秒，开发者必须在自己的服务全局缓存jsapi_ticket

        //读取缓存
        $ticket = RedisUtil::getInstance(RedisKeyEnum::WECHAT_OFFICIAL_TICKET)->get();

        //过期或强制刷新时，需重新请求
        if (!$ticket || $refresh) {
            //读取access_token
            if (!$this->getAccessTtoken()) {
                return $this->error($this->getMessage(), $this->getData());
            }
            $access_token = $this->getData();
            $data = [
                'access_token' => $access_token,
                'type'         => 'jsapi',
            ];
            $response = $this->getJson("/cgi-bin/ticket/getticket", $data);
            if ($response->getStatusCode() != 200) {
                return $this->error($response->getReasonPhrase());
            }

            $json = $response->getBody()->getContents();
            $body = json_decode($json, true);
            if (!isset($body['errcode']) || $body['errcode'] != 0) {
                return $this->error(!empty($body['errmsg']) ? $body['errmsg'] : 'Error_0', $body);
            }

            //缓存存储
            $ticket = $body['ticket'];
            RedisUtil::getInstance(RedisKeyEnum::WECHAT_OFFICIAL_TICKET)->setex($ticket, 7000);
        }

        return $this->success('获取成功', $ticket);
    }

    /**
     * 获取微信基础access_token
     * @param bool $refresh 是否刷新
     * @return string
     */
    public function getAccessTtoken(bool $refresh = false)
    {
        //有效期一般为7200秒，开发者必须在自己的服务全局缓存access_token
        //此处保存的access_token为基础的，并非授权的(授权的需要实时获取)

        $access_token = RedisUtil::getInstance(RedisKeyEnum::WECHAT_OFFICIAL_ACCESS_TOKEN)->get();

        //过期或强制刷新时，需重新请求
        if (!$access_token || $refresh) {
            $data = [
                'appid'      => $this->config['app_id'],
                'secret'     => $this->config['secret'],
                'grant_type' => 'client_credential',
            ];
            $response = $this->getJson("/cgi-bin/token", $data);
            if ($response->getStatusCode() != 200) {
                return $this->error($response->getReasonPhrase());
            }

            $json = $response->getBody()->getContents();
            $body = json_decode($json, true);
            if (!isset($body['access_token'])) {
                return $this->error(!empty($body['errmsg']) ? $body['errmsg'] : 'Error_0', $body);
            }

            $access_token = $body['access_token'];
            RedisUtil::getInstance(RedisKeyEnum::WECHAT_OFFICIAL_ACCESS_TOKEN)->setex($access_token, 7000);
        }

        return $this->success('获取成功', $access_token);
    }

    /**
     * 推送模板消息
     *
     * @param string $template_id
     * @param string $openid
     * @param array $data 模版数据
     * @param string $link 跳转链接
     * @return bool
     * @throws GuzzleException
     */
    public function templateMsg(string $template_id, string $openid, array $data, string $link = '')
    {
        //读取access_token
        if (!$this->getAccessTtoken()) {
            return $this->error($this->getMessage(), $this->getData());
        }
        $access_token = $this->getData();

        $data = [
            'touser'      => $openid,
            'template_id' => $template_id,
            'url'         => $link,
            'data'        => $data
        ];
        $response = $this->postJson("cgi-bin/message/template/send?access_token={$access_token}", $data);
        if ($response->getStatusCode() != 200) {
            return $this->error($response->getReasonPhrase());
        }

        $json = $response->getBody()->getContents();
        $body = json_decode($json, true);
        if (empty($body['errmsg']) || $body['errmsg'] != 'ok') {
            return $this->error(!empty($body['errmsg']) ? $body['errmsg'] : 'Error_0', $body);
        }

        return $this->success('获取成功', $body);
    }
}